Index  Decision Records ADR-182: Velocity Chart on the Decision Records Overview

ADR-182: Velocity Chart on the Decision Records Overview

1 Status

Date Status
23-05-2026 Proposed
23-05-2026 Accepted
23-05-2026 In-Progress
23-05-2026 Implemented

2 Context

The Decision Records Overview page reserves three chart cells inside decisions_overview_charts. The first cell holds the "Decision Records by Type" pie chart introduced by adr-177-overview-pie-chart. The remaining two cells are placeholders.

A pie chart conveys the current distribution of records by type, but it says nothing about how the project is moving through the workflow. A reader scanning the overview cannot tell whether the team has been accumulating Proposed records faster than they reach Implemented, whether work has stalled in In-Progress, or whether the past six weeks were productive at all. That signal already exists in every decision record's # Status table — each row records the date a status was reached — but it is not surfaced anywhere on the overview.

This ADR adds a second chart that shows, week by week for the last six weeks, how many decision records were in each status as of that week's Friday. Friday is chosen as the snapshot date because it is the conventional Western end-of-week marker and aligns with how progress is typically reported.

Different decision records may use different status vocabularies (a strict ADR may use Proposed → Accepted → In-Progress → Implemented; an issue record may use New → Investigating → Fixed; an enhancement may use Proposed → Rejected). The chart cannot assume a single canonical workflow. The combined picture is built as a dictionary: every distinct status text encountered across all records becomes its own stack segment, and the bar for a given Friday simply counts how many records were in each status on that day.

3 Decision

Add a second chart to the Decision Records Overview page — a Chart.js stacked bar chart titled "Decision Records by Status Over Time" — placed in the second chart_cell of the existing decisions_overview_charts grid (the third cell remains reserved).

3.1 Snapshot dates

The chart shows six bars corresponding to the six most recent Fridays on or before the date of rendering, ordered left-to-right oldest-to-newest. The most recent Friday is the largest calendar date that is both a Friday and not later than the build date; the remaining five are the five Fridays before it at seven-day intervals.

For a build performed on 23-05-2026 (Saturday), the six Fridays are: 17-04-2026, 24-04-2026, 01-05-2026, 08-05-2026, 15-05-2026, 22-05-2026.

The X-axis labels render in the same DD-MM-YYYY format already used elsewhere in Almirah for dates.

3.2 Status as of a given Friday

For each decision record and each of the six Fridays, the gem computes the record's effective status as of that Friday using this rule:

  1. Take the record's Status table (found by locating the Status section heading, reusing the existing find_section_table helper).
  2. From the table's rows, keep those whose Date column contains a value parseable as DD-MM-YYYY and whose parsed date is on or before the Friday.
  3. Among the kept rows, pick the one with the latest parsed date.
  4. If two or more kept rows share that latest date, pick the one that appears later in the table (document order).
  5. The picked row's Status column value, after stripping whitespace, is the record's effective status as of that Friday.

If no row qualifies — i.e. every parseable date in the table is after the Friday — the record did not yet exist as of that Friday and does not contribute to that bar.

If the record has no Status section, no table in that section, or no rows with parseable dates, the record contributes to no bar of the chart (it is skipped entirely for the velocity chart, even though it still appears in the row listing below).

The future-dated Implemented row that authors often write up-front for planning is handled naturally by this rule: it is simply ignored on Fridays that precede its date.

3.3 Combined workflow (status dictionary)

The chart does not assume a fixed status vocabulary. For each Friday, the gem builds a dictionary keyed by status text — incrementing the count for an existing key and adding a new key for an as-yet-unseen status. The set of stack segments in the chart is the union of all keys across all six Fridays' dictionaries.

Status values are compared by exact, case-sensitive text equality after stripping surrounding whitespace. Proposed and proposed are different segments; Implemented and Implemented (trailing space) are the same.

Segment ordering: segments are stacked in first-seen order, computed by walking the six Fridays oldest-first and, within each Friday, walking decision records in parse order. This produces a stable, deterministic order without imposing a canonical workflow — which is what the user requested ("the order does not matter"). A future enhancement may revisit the ordering if a particular convention emerges.

3.4 Rendering

The chart is rendered inline in the overview HTML using Chart.js (already loaded for the existing pie chart). Configuration:

Chart.js' default colour palette is used. The dataset's backgroundColor and borderColor are left unset so the Chart.js v4 Colors plugin auto-fills them: each dataset gets a translucent (alpha 0.5) fill paired with a fully opaque border. Because the bar element's default borderWidth is 0, only the soft pastel fill is visible.

3.5 Visual parity with the existing pie chart

The pie chart introduced by adr-177-overview-pie-chart was rendered with the Chart.js defaults for a single-dataset categorical chart, which means each slice's backgroundColor defaulted to a fully opaque colour from the palette. Sitting next to the new velocity chart's soft pastel bars, the saturated pie slices stood out as visibly darker even though both charts use the same palette.

To bring the two charts into visual parity, this ADR also defines a small shared palette helper and applies it to the pie dataset:

The result: pie slices and stacked-bar segments use exactly the same colours at the same opacity, with no visible borders in either chart. The shared palette is private to DecisionsOverview; it is not a public theming surface.

4 Scope

Item Status Start Date Target Date Description
Requirements Done 23-05-2026 23-05-2026 New SRS items in srs.md covering: the velocity chart placement in the second chart cell of the Decision Records Overview; the six-Friday window ending with the latest Friday on or before the build date; the status-as-of-Friday algorithm (latest parseable date ≤ Friday, document-order tie-break); the pre-existence rule (record skipped on Fridays before its earliest dated row); the missing-Status-table rule (record skipped for the chart); the combined-workflow dictionary built by exact case-sensitive text match
Code Done 23-05-2026 23-05-2026 Add an effective_status_on(date) method on Decision that reuses find_section_table('Status') and applies the latest-date/document-order rule; add a private helper on DecisionsOverview that computes the six Fridays from Date.today, builds the per-Friday status dictionaries, derives the union of status segments in first-seen order, and emits the Chart.js stacked-bar config into the second chart_cell; ensure the third chart_cell remains an empty placeholder; add a CHART_PALETTE constant and a private palette_rgba(index, alpha) helper, and use them to emit the pie chart's dataset with an explicit half-opacity backgroundColor array and borderWidth: 0 so the pie's colour treatment matches the stacked-bar chart
Tests Done 23-05-2026 23-05-2026 End-to-end tests under spec/e2e/decisions_spec.rb: the velocity chart canvas is emitted in the second chart cell; the chart type is bar and both axes are stacked; the labels array contains six DD-MM-YYYY strings; six bars are rendered; the latest-date tie-break by document order works; future-dated rows are ignored before their date; pre-existence (Friday before first row) leaves the record off the bar; missing Status table leaves the record off every bar; mixed-workflow records contribute to separate segments; unknown status text becomes its own segment without raising

5 Out of Scope

6 Consequences

6.1 Positive

6.2 Negative

6.3 Neutral

7 Alternatives Considered

8 Software Versions

Software Version Category Software Version ID
Latest Released Version 0.3.1
Issue Found in Version 0.4.0
Target Release Version 0.4.0

9 Affected Documents

# Proposed Text Req-ID
1 The Decision Records Overview page shall include a stacked bar chart visualising decision-record counts grouped by status across a trailing six-week window, placed in the second chart cell of the Decision Records Overview charts grid. SRS-071
2 The velocity chart shall display six bars, one per Friday, corresponding to the six most recent Fridays on or before the date the page is rendered, ordered left-to-right oldest-to-newest, with each X-axis label formatted as "DD-MM-YYYY". SRS-072
3 The status of a Decision Record as of a given calendar date shall be computed as the Status table row whose parseable date is the latest one that is on or before that calendar date; when multiple rows share that latest date, the row appearing later in the table shall be selected. SRS-073
4 When the earliest parseable Status table date of a Decision Record is later than the calendar date of a velocity chart bar, that Decision Record shall not contribute to that bar. SRS-074
5 Decision Records that have no Status section, no table in their Status section, or no Status table row whose Date cell is parseable as "DD-MM-YYYY" shall not contribute to any bar of the velocity chart. SRS-075
6 The set of stack segments in the velocity chart shall be the union of every distinct status text encountered across all Decision Records and all six bars, with status text compared by exact case-sensitive equality after stripping surrounding whitespace. SRS-076

10 References

11 Review Evidences